<?php

class TableDataGateway
{

  /**
   *
   * @var string
   */
  protected $table;

  /**
   *
   * @var string
   */
  protected $pk;

  /**
   *
   * @var array
   */
  protected static $instances = array();

  public function __construct($table, $pk)
  {
    $this->table = $table;
    $this->pk = $pk;
  }

  /**
   *
   * @param string $table
   * @param string $pk
   * @return TableDataGateway
   */
  public static function getInstance($table, $pk = null)
  {
    if (! isset(self::$instances[$table])) {
      self::$instances[$table] = new self($table, $pk);
    }

    return self::$instances[$table];
  }

  public function move($field, $id, $direction)
  {
    $data = array();
    $dif = 0;
    $dis = 0;

    if ($direction == 'top') {
      $row = $this->find($id);
      $pos = sy_get_param($row, $field, 0);

      $data = DAO::getInstance()->query()->from($this->table)->select($this->pk, $field)->where("$field < $pos")->execute()->fetchAll();
      $data = sy_array_to_options($data, $this->pk, $field);

      $dif = -1;
      $dis = - count($data);
    }
    elseif ($direction == 'up') {
      $dif = -1;
      $dis = -1;

      $row = $this->find($id);
      $pos = sy_get_param($row, $field, 0);

      $data = DAO::getInstance()->query()->from($this->table)->select($this->pk, $field)->where("$field < $pos")->limit(1)->orderBy("$field DESC")->execute()->fetchAll();
      $data = sy_array_to_options($data, $this->pk, $field);
    }
    elseif ($direction == 'down') {
      $dif = 1;
      $dis = 1;

      $row = $this->find($id);
      $pos = sy_get_param($row, $field, 0);

      $data = DAO::getInstance()->query()->from($this->table)->select($this->pk, $field)->where("$field > $pos")->limit(1)->orderBy("$field ASC")->execute()->fetchAll();
      $data = sy_array_to_options($data, $this->pk, $field);
    }
    elseif ($direction == 'bottom') {
      $row = $this->find($id);
      $pos = sy_get_param($row, $field, 0);

      $data = DAO::getInstance()->query()->from($this->table)->select($this->pk, $field)->where("$field > $pos")->execute()->fetchAll();
      $data = sy_array_to_options($data, $this->pk, $field);

      $dif = 1;
      $dis = count($data);
    }

    if (! empty($data)) {
      $sql = "UPDATE $this->table SET $field = $field - $dif WHERE $this->pk IN (".implode(', ', array_keys($data)).")";
      DAO::getInstance()->query($sql)->execute();

      $sql = "UPDATE $this->table SET $field = $field + $dis WHERE $this->pk = $id";
      DAO::getInstance()->query($sql)->execute();
    }
  }

  /**
   *
   * @param int $offset
   * @param int $limit
   * @return Pager
   */
  public function findPager($offset = null, $limit = null, $params = null)
  {
    return new Pager($this->findCount($offset, $limit, $params), $limit, $offset);
  }

  /**
   *
   * @param int $id
   * @return array
   */
  public function find($id, $params = null)
  {
    $query = DAO::getInstance()->query()->from($this->table)->where("$this->pk = ?");

    $this->setFields($query, $params);
    $this->setWhere($query, $params);
    $this->setOrderBy($query, $params);
    $this->setGroupBy($query, $params);
    $this->setHaving($query, $params);

    $result = $query->execute($id)->fetchRow();

    return $result;
  }

  /**
   *
   * @param int $offset
   * @param int $limit
   * @return int
   */
  public function findCount($offset = null, $limit = null, $params = null)
  {
    if (is_array($offset)) {
      $params = $offset;
      $offset = null;
    }
    
    $query = DAO::getInstance()->query()->from($this->table)->select("COUNT($this->pk)")->offset($offset)->limit($limit);

    $data = null;

    if (! empty($params)) {
      $this->setFields($query, $params);
      $this->setWhere($query, $params);
      $this->setOrderBy($query, $params);
      $this->setGroupBy($query, $params);
      $this->setHaving($query, $params);

      if (isset($params['data'])) {
        $data = $params['data'];
      }
    }

    $result = $query->execute($data)->fetchOne();

    return (int)$result;
  }

  /**
   *
   * @param int $offset
   * @param int $limit
   * @return array
   */
  public function findAll($offset = null, $limit = null, $params = null)
  {
    $query = DAO::getInstance()->query()->from($this->table)->offset($offset)->limit($limit);

    $data = null;

    if (! empty($params)) {
      $this->setFields($query, $params);
      $this->setWhere($query, $params);
      $this->setOrderBy($query, $params);
      $this->setGroupBy($query, $params);
      $this->setHaving($query, $params);

      if (isset($params['data'])) {
        $data = $params['data'];
      }
    }

    $result = $query->execute($data)->fetchAll();

    return $result;
  }

  /**
   *
   * @param int $id
   * @return int
   */
  public function delete(&$data)
  {
    $id = sy_get_param($data, $this->pk);
    $result = DAO::getInstance()->query("DELETE FROM $this->table WHERE $this->pk = ?")->execute($id);
    return $result->numRows();
  }

  /**
   *
   * @param array $data
   * @return mixed
   */
  public function save(&$data)
  {
    $id = sy_get_param($data, $this->pk);

    if (empty($id)) {
      return $this->insert($data);
    }
    else {
      return $this->update($data);
    }
  }

  /**
   *
   * @param array $data
   * @return void
   */
  public function insert(&$data)
  {
    $_data = $data;

    DAO::getInstance()->query("INSERT INTO $this->table " . QueryObject::buildInsert($_data))->execute($data);

    $data[$this->pk] = DAO::getInstance()->lastInsertId();
  }

  /**
   *
   * @param mixed $data
   * @return int
   */
  public function update(&$data)
  {
    $_data = $data;

    $id = sy_get_param($data, $this->pk);

    unset($_data[$this->pk]);

    $result = 0;

    if (! empty($_data)) {
      $result = DAO::getInstance()->query("UPDATE $this->table " . QueryObject::buildUpdate($_data) . " WHERE $this->pk = $id")->execute($_data)->numRows();
    }

    return $result;
  }

  protected function setFields(IQueryObject $query, array $params = null)
  {
    if (isset($params['fields'])) {
      $query->select($params['fields']);
    }
  }

  protected function setOrderBy(IQueryObject $query, array $params = null)
  {
    if (isset($params['orderBy'])) {
      $query->orderBy($params['orderBy']);
    }
  }

  protected function setLimit(IQueryObject $query, array $params = null)
  {
    if (isset($params['limit'])) {
      $query->limit($params['limit']);
    }
  }

  protected function setOffset(IQueryObject $query, array $params = null)
  {
    if (isset($params['offset'])) {
      $query->offset($params['offset']);
    }
  }

  protected function setWhere(IQueryObject $query, array $params = null)
  {
    if (isset($params['where'])) {
      if (is_array($params['where'])) {
        foreach ($params['where'] as $where) {
          $query->where($where);
        }
      }
      else {
        $query->where($params['where']);
      }
    }
  }

  protected function setGroupBy(IQueryObject $query, array $params = null)
  {
    if (isset($params['groupBy'])) {
      $query->offset($params['groupBy']);
    }
  }

  protected function setHaving(IQueryObject $query, array $params = null)
  {
    if (isset($params['having'])) {
      $query->offset($params['having']);
    }
  }

}

?>